////////////////////////////////////////////////////////////////////////////////
// THIS HEADER SHOULD DEPEND ON NOTHING BUT STL, OpenGL, and AntTweakBar
////////////////////////////////////////////////////////////////////////////////
#ifdef __APPLE__
#define _MACOSX
#endif
#ifdef PREVIEW3D_NO_ANTTWEAKBAR
#define TW_CALL
#else
#include "AntTweakBar.h"
#endif
#include <vector>
#ifndef PREVIEW3D_NO_SHADERS
#include "shaders.h"
#endif

class MatlabIO;

typedef std::vector<double> Vector;
//class Vector
//{
//  public:
//    double coords[3];
//    double & operator[](int i) { return coords[i]; }
//    const double & operator[](int i) const { return coords[i]; }
//    void resize(int i){}
//    void resize(int i,double d){}
//
//};

class Preview3D{

#ifndef PREVIEW3D_NO_SHADERS
  public:
    enum ShaderMode{
      OFF,
      DIRECTIONAL_PER_PIXEL
      //ISOLINES,
    };
#define NUM_SHADER_MODE 2
#endif
  //////////////////////////////////////////////////////////////////////////////
  //
  // Instance methods/fields (to be plugged into an OpenGL context)
  //
  //////////////////////////////////////////////////////////////////////////////
  public:
    ////////////////////////////////////////////////////////////////////////////
    //
    // Instance variables
    //
    ////////////////////////////////////////////////////////////////////////////
    // Vertices of the current mesh
    std::vector<Vector > vertices;
    // number of vertices in mesh should == vertices.size(), I need this as a
    // separate variable so I can display it in AntTweakBar
    size_t number_of_vertices;
    // Face list of current mesh
    std::vector<std::vector<size_t> > faces;
    // OpenGL Display list of mesh
    int faces_display_list;
    // number of faces in mesh should == faces.size(), I need this as a
    // separate variable so I can display it in AntTweakBar
    size_t number_of_faces;
	// delete the opened file on exit
	bool delete_on_exit;
    // One normal per face
    std::vector<Vector > face_normals;
    // One color per face
    std::vector<Vector > face_colors;
    // One normal per vertex
    std::vector<Vector > vertex_normals;
    // One color per vertex
    std::vector<Vector > vertex_colors;
    // number of frames per second
    double fps;
    // Scene frustum shift
    double frustum_shift_x;
    // Scene scale
    float g_Zoom;
    // zoom and shift are set once when mesh is loaded to center and normalize
    // shape to fit unit box
    float zoom;
    float shift[3]; 
    // Shape orientation (stored as a quaternion)
    float g_Rotation[4];
    // Auto rotate
    bool g_AutoRotate;
    bool auto_rotate_restart;
    int g_RotateTime;
    float g_RotateStart[4];
    // snap to nearest canonical view quaternion
    double trackball_snap_to_canonical;
    // Shapes material
    // Grey
    //float g_MatAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f };
    //float g_MatDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    // Gold
    float g_MatAmbient[4];
    float g_MatDiffuse[4];
    float g_MatSpecular[4];
    float g_MatShininess;
    float background_color[4];
    float line_color[4];
    // Light parameter
    float g_LightMultiplier;
    float g_LightDirection[3];
    bool is_compiled;
    bool flat_shading;
    bool flat_shading_at_compile;
    bool show_faces;
    bool show_lines;
    bool invert_normals;
    bool invert_normals_at_compile;
    // Screen events
    int width;
    int height;
    // Mouse events
    bool down;
    int last_x;
    int last_y;
    int down_x;
    int down_y;
    float scroll_position;
    float down_rotation[4];
    // Shaders
#ifndef PREVIEW3D_NO_SHADERS
    struct GLSL_Program s_directionalPerPixelProgram;
    struct GLSL_Program s_isolinesProgram;
    ShaderMode shader_mode;
#endif
	// Path of the temporary file to delete
	std::string file_to_delete;
		
    ////////////////////////////////////////////////////////////////////////////
    // Initialization
    ////////////////////////////////////////////////////////////////////////////
    // Needs time at initialization so that auto-rotation can animate
    Preview3D(int current_time, bool deleteonexit = true);
    bool load_mesh_from_file(const char* mesh_file_name);

    ////////////////////////////////////////////////////////////////////////////
    // De-initialization
    ////////////////////////////////////////////////////////////////////////////
    ~Preview3D();
    bool save_mesh_to_file(const char* mesh_file_name);

    ////////////////////////////////////////////////////////////////////////////
    // Handle key/mouse events
    ////////////////////////////////////////////////////////////////////////////
    bool key_down(unsigned char key, int mouse_x, int mouse_y);
    bool key_up(unsigned char key, int mouse_x, int mouse_y);
    bool mouse_down(int mouse_x, int mouse_y);
    bool mouse_up(int mouse_x, int mouse_y);
    bool mouse_move(int mouse_x, int mouse_y);
    bool mouse_scroll(int mouse_x, int mouse_y, float delta_y);
    void gather_modifiers(
        int glutButton, 
        int modifiers, 
        bool & shift_down,
        bool & control_down, 
        bool & option_down);

    ////////////////////////////////////////////////////////////////////////////
    // Main-loop draw scene 
    ////////////////////////////////////////////////////////////////////////////
    void draw(int current_time);
    // push scene matrices
    void push_scene(int current_time);
    // pop scene matrices
    void pop_scene();

    ////////////////////////////////////////////////////////////////////////////
    // OpenGL context resize
    ////////////////////////////////////////////////////////////////////////////
    void resize(int width, int height);

    ////////////////////////////////////////////////////////////////////////////
    // Autorotate getters and setters
    ////////////////////////////////////////////////////////////////////////////
    void SetAutoRotate(int value);
    int GetAutoRotate();
    void view_xy_plane();
    void view_xz_plane();
    void view_yz_plane();


  //////////////////////////////////////////////////////////////////////////////
  //
  // Static utility methods
  //
  //////////////////////////////////////////////////////////////////////////////
  public:
    ////////////////////////////////////////////////////////////////////////////
    // VIEW
    ////////////////////////////////////////////////////////////////////////////

    // Snap the quaternion q to the nearest canonical view quaternion
    // Input:
    //   q  quaternion to be snapped (also see Outputs)
    //   threshold  (optional) threshold:
    //     1.0 --> snap any input
    //     0.5 --> snap inputs somewhat close to canonical views
    //     0.0 --> snap no input
    // Output:
    //   q  quaternion possibly set to nearest canonical view
    // Return:
    //   true only if q was snapped to the nearest canonical view
    static bool snap_to_canonical_quaternion(
      float q[4],
      const double threshold=1.0);

    ////////////////////////////////////////////////////////////////////////////
    // File IO
    ////////////////////////////////////////////////////////////////////////////
    static bool read_obj(
        const char* obj_file_name,
        std::vector<Vector > & vertices, 
        std::vector<std::vector<size_t> > & faces);
    static bool read_off(
        const char* off_file_name,
        std::vector<Vector > & vertices, 
        std::vector<std::vector<size_t> > & faces);
    // Read medit .mesh file format into a list of vertices, faces and
    // triangles.  Faces are not necessarily surface faces though this is 
    static bool read_mesh(
        const char * mesh_file_name,
        std::vector<Vector > & vertices,
        std::vector<std::vector<size_t> > & triangles,
        std::vector<std::vector<size_t> > & tetrahedra);
    static bool read_wrl(
        const char * wrl_file_name,
        std::vector<Vector > & vertices, 
        std::vector<std::vector<size_t> > & faces);
#ifndef _NOMATLAB_
	static bool read_mat(const char* mat_file_name, MatlabIO& mio);
#endif
	
	
    static bool write_off(
        const char* off_file_name,
        const std::vector<Vector > & vertices, 
        const std::vector<std::vector<size_t> > & faces);
    static bool write_obj(
        const char* obj_file_name,
        const std::vector<Vector > & vertices, 
        const std::vector<std::vector<size_t> > & faces);

    ////////////////////////////////////////////////////////////////////////////
    // Rendering helpers
    ////////////////////////////////////////////////////////////////////////////
    // draw a mesh
    static void draw_mesh(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
      const std::vector<Vector > & vertex_normals, 
      const std::vector<Vector > & vertex_colors, 
	  const std::vector<Vector > & face_normals, 
	  const std::vector<Vector > & face_colors, 
      
      const bool show_lines,
      const float line_color[4],
      const bool show_faces,
      int faces_display_list);
    // Compile mesh into opengl display list
    static void compile_mesh(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
      const std::vector<Vector > & vertex_normals, 
 	  const std::vector<Vector > & vertex_colors, 
      const std::vector<Vector > & face_normals, 
 	  const std::vector<Vector > & face_colors, 
      const bool flat_shading,
      const bool invert_normals,
      int & faces_display_list);
    // Compile strictly quad mesh into opengl display list
    static void compile_triangle_mesh(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
	  const std::vector<Vector > & vertex_normals, 
	  const std::vector<Vector > & vertex_colors, 
	  const std::vector<Vector > & face_normals, 
	  const std::vector<Vector > & face_colors, 
	  const bool flat_shading,
      const bool invert_normals,
      int & faces_display_list);
    // Compile strictly triangle mesh into opengl display list
    static void compile_quad_mesh(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
	  const std::vector<Vector > & vertex_normals, 
	  const std::vector<Vector > & vertex_colors, 
	  const std::vector<Vector > & face_normals, 
	  const std::vector<Vector > & face_colors, 
	  const bool flat_shading,
      const bool invert_normals,
      int & faces_display_list);
    // Compile strictly triangle mesh into opengl display list
    static void compile_polygon_mesh(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
	  const std::vector<Vector > & vertex_normals, 
	  const std::vector<Vector > & vertex_colors, 
	  const std::vector<Vector > & face_normals, 
	  const std::vector<Vector > & face_colors, 
	  const bool flat_shading,
      const bool invert_normals,
      int & faces_display_list);
    static void draw_floor();

    ////////////////////////////////////////////////////////////////////////////
    // Mesh computation helpers
    ////////////////////////////////////////////////////////////////////////////
    // Computes a normal for each face
    static void compute_face_normals(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
      std::vector<Vector > & face_normals);
    // Computes a normal for each vertex
    static void compute_vertex_normals(
      const std::vector<Vector > & vertices, 
      const std::vector<std::vector<size_t> > & faces,
      std::vector<Vector > & vertex_normals);
    // Compute bounding box and centroid
    static void compute_bounding_box_and_centroid(
      const std::vector<Vector > & vertices, 
      Vector & min_point,
      Vector & max_point,
      Vector & centroid);
    // Determines how much to zoom and shift such that the mesh fills the unit
    // box (centered at the origin)
    static void get_scale_and_shift_to_fit_mesh(
      const std::vector<Vector > & vertices, 
      float & zoom,
      float shift[]);
    // Returns true if normals are inverted
    static bool test_for_inverted_normals(
      const std::vector<Vector > & vertices, 
      const std::vector<Vector > & vertex_normals);
    // Returns true if all faces are triangles
    static bool faces_are_triangles(
      const std::vector<std::vector<size_t> > & faces);
    // Returns true if all faces are quads
    static bool faces_are_quads(
      const std::vector<std::vector<size_t> > & faces);


    ////////////////////////////////////////////////////////////////////////////
    // Convenient Math helper functions (used to convert anttweakbar trackball
    // to opengl matrices)
    ////////////////////////////////////////////////////////////////////////////
    static void SetQuaternionFromAxisAngle(const float *axis, float angle, float *quat);
    static void ConvertQuaternionToMatrix(const float *quat, float *mat);
    static void MultiplyQuaternions(const float *q1, const float *q2, float *qout);
    static void CopyArray3(float a[], float b[3])
    {
      b[0] = a[0];
      b[1] = a[1];
      b[2] = a[2];
    };
    static void CopyArray4(float a[], float b[4])
    {
      b[0] = a[0];
      b[1] = a[1];
      b[2] = a[2];
      b[3] = a[3];
    };

    ////////////////////////////////////////////////////////////////////////////
    // Autorotate callbacks 
    ////////////////////////////////////////////////////////////////////////////
    //  Callback function called when the 'AutoRotate' variable value of the tweak bar has changed
    static void TW_CALL SetAutoRotateCB(const void *value, void *clientData);
    //  Callback function called by the tweak bar to get the 'AutoRotate' value
    static void TW_CALL GetAutoRotateCB(void *value, void *clientData);
    static void TW_CALL view_xy_planeCB(void *clientData);
    static void TW_CALL view_xz_planeCB(void *clientData);
    static void TW_CALL view_yz_planeCB(void *clientData);
    static void TW_CALL snap_to_canonical_quaternionCB(void *clientData);
};
